package com.woniuxy.qimenggateway.filter;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.woniuxy.qimengcommons.enums.RespStatus;
import com.woniuxy.qimengcommons.exception.TokenIllegalException;
import com.woniuxy.qimengcommons.util.JwtUtil;
import com.woniuxy.qimengcommons.util.PayLoad;
import com.woniuxy.qimenggateway.util.RequestUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 定义一个认证过滤器
 * 该过滤器，需要执行在路由之前
 * 所有我们需要使用Pre类型的过滤器
 */
@Component
public class AuthenticationFilter extends ZuulFilter {

    @Resource
    private JwtUtil jwtUtil;

    /**
     * 设定过滤器的类型
     * PRE_TYPE 前置过滤器
     * ROUTE_TYPE 路由过滤器
     * POST_TYPE 路由过后的过滤器，执行在微服务返回响应的时候
     * ERROR_TYPE 错误过滤器
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 设定过滤器的执行顺序
     * PRE_DECORATION_FILTER_ORDER 默认是5
     * 该返回的值，值越小，对应的过滤器越先执行
     * PRE_DECORATION_FILTER_ORDER -1 为了执行在内置过滤器之前，最好-1
     * @return
     */
    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER -1;
    }

    /**
     * 判定哪些路径需要过滤
     * false 表示不做过滤
     * true 表示需要过滤
     * @return
     */
    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        //通过request 获得请求路径
        String uri = request.getRequestURI();
        System.out.println(uri);
        //   /microserver-auth/sys/login   登录|注册 是不需要过滤器   false
        //  /microserver-shop/shop/more
        if(uri.equals("/microserver-auth/sys/login") || uri.equals("/microserver-auth/user/more")){
            return false;
        }else{
            return true;
        }
    }

    /**
     * 具体的过滤规则
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String token = RequestUtil.getCookie("token",request);
        //判断token是否为空
        if(StringUtils.hasLength(token)){
            //校验是否令牌合格
            validateTokenLegal(token,requestContext);
        }else{
            //从请求头中 再次去获取Token 的值
            token = RequestUtil.getHeader("token",request);
            if(StringUtils.hasLength(token)) {
                //校验是否令牌合格
                validateTokenLegal(token,requestContext);
            }else{
                //令牌没获取到，返回前端：401 没有认证
                illegalHanlder(requestContext, HttpStatus.UNAUTHORIZED, RespStatus.NO_AUTHENTICATION.getMessage());
            }
        }
        return null;
    }

    /**
     * 令牌的认证方法，认证成功就路由，认证失败就返回客户端信息
     * @param token 令牌
     * @param requestContext 请求作用域
     */
    private void validateTokenLegal(String token, RequestContext requestContext) {

        try{
            //校验令牌是否有效
            PayLoad payLoad = jwtUtil.validateToken(token);
            //判断令牌是否需要刷新
            String newToken = jwtUtil.reflushToken(payLoad,token);

            if(StringUtils.hasLength(newToken)){
                //将Token通过请求头返回给前端 (newToken)
                requestContext.getResponse().addHeader("newToken",newToken);
            }

            //可能抛出加密算法不匹配异常
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            illegalHanlder(requestContext, HttpStatus.FORBIDDEN, RespStatus.AUTH_FAILED.getMessage());
            //可能抛出令牌失效异常
        }catch (TokenExpiredException e){
            illegalHanlder(requestContext, HttpStatus.FORBIDDEN, RespStatus.TOKEN_EXPIRED.getMessage());
        }catch (TokenIllegalException e){
            illegalHanlder(requestContext, HttpStatus.FORBIDDEN, RespStatus.TOKEN_ILLEGAL.getMessage());
        } catch (Exception e){
            e.printStackTrace();
            illegalHanlder(requestContext, HttpStatus.FORBIDDEN, RespStatus.AUTH_FAILED.getMessage());
        }
    }


    /**
     * 没传递Token令牌的情况下，返回结果
     * @param requestContext 请求作用域
     * @param msg 返回的消息
     */
    private void illegalHanlder(RequestContext requestContext, HttpStatus status, String msg) {
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(status.value());//返回状态码401 没有认证
        if(StringUtils.hasLength(msg)){
            ObjectMapper om = new ObjectMapper();
            try{
                HttpServletRequest request = requestContext.getRequest();
                requestContext.setResponseBody(om.writeValueAsString(msg));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
